#include <limits>
#include "Node.h"
#include "Mailer.h"

vector<int> StringToVector(string s) {
	int i, num = 0, mul = 1;
	vector<int> ans;
	//	cout<<"string is: "<<s<<endl;
	for (i = 0; i < s.size(); i++) {
		if (s[i] == ' ') {
			mul = 1;
			ans.push_back(num);
			num = 0;
		} else {
			num *= mul;
			num += (s[i] - '0');
			mul *= 10;
		}
	}
	//	cout<<"last num is: "<<num<<endl;
	//cout<<"neighbors: ";
	ans.push_back(num);
	/*for (vector<int>::iterator iter = ans.begin();iter!= ans.end(); iter++)
	 cout<<*iter<<" ";
	 cout<<endl;*/
	return ans;

}

Node::Node(int id, string neighbors, int numOfNodes, Mailer* mailer) :
	OSThread(), id(id), accumulator(0), quantaAccumulator(0) {
//	cout << "creating: " << this->id << endl;
	this->mailer = mailer;
	//	int i = 0;
	this->stable = false;
	this->active = true;
	this->blocked = false;
	this->yielded = false;
	this->priority = 0;
	vector<int> VN = StringToVector(neighbors);
	//update neighbors from neighbors string:
	for (vector<int>::iterator iter = VN.begin(); iter != VN.end(); iter++)
		this->neighbors.push_back(*iter);
	/*cout<<"neighbors :";
	 for (vector<int>::iterator iter = VN.begin(); iter!=VN.end(); iter++)
	 cout<<*iter<<" ";
	 cout<<endl;*/
	//create RT table:
	//put only me in RT table

	//  put all nodes (1-numOfNodes) into RT table
	resetRT(numOfNodes);
	//set Quanta to 0
	this->setQuanta(0);
}

void Node::resetRT(int num) {
	for (int i = 1; i <= num; i++)
		this->RT[i] = make_pair(((i == id) ? 0
				: std::numeric_limits<int>::max()), (i == id ? id : 0));
}

Node::~Node() {
//	cout << "des'tor of node:" << this->id << endl;
}

int Node::getID() {
	return this->id;
}

vector<int> Node::getNeighbors() {
	return this->neighbors;
}

map<int, pair<int, int> > Node::getRT() {
	return this->RT;
}

string Node::toString() {
	stringstream result;
	result << this->printRT();
	result << "neighbors: ";
	for (viter iter = this->neighbors.begin(); iter != this->neighbors.end(); iter++)
		result << *iter << " ";
	result << endl;
	return result.str();
}

string Node::printRT() {
	stringstream result;
	result << "node: " << this->id << endl;
	result << "RT:" << endl;
	result << "+================+" << endl;
	for (miter iter = this->RT.begin(); iter != this->RT.end(); iter++)
		result << "|<" << (*iter).first << "," << (*iter).second.first << ","
				<< (*iter).second.second << ">|" << endl;
	result << "+================+" << endl;
	return result.str();

}

void Node::updateRT(map<int, pair<int, int> > *other, int senderID) {
	bool changed = false;
//	cout << this->id << " RT before:" << endl;
//	cout << printRT() << endl;
	map<int, pair<int, int> >::iterator i = this->RT.begin();

	for (; i != this->RT.end(); i++) {

		if ((i->first) != this->id) {

			map<int, pair<int, int> >::iterator j = other->find(i->first);
			pair<int, int> mine = i->second;
			pair<int, int> his = j->second;
			int myDistance = mine.first;
			int hisDistance = his.first;
			if (hisDistance != std::numeric_limits<int>::max())
				if (myDistance > hisDistance+1) {
//					cout << "myDistance = " << myDistance << " hisDistance = "
//							<< hisDistance << endl;
					pair<int, int> p = make_pair(hisDistance + 1, senderID);
					if (RT[i->first] == p) {
						//changed = false;
					} else {
						RT[i->first] = p;
						changed = true;
					}

				}
		}
	}
	delete other;
//	cout << this->id << " after:" << endl;
//	cout << printRT() << endl;
	this->stable = !changed;
	if (changed) {
//		cout << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!" << this->id
//				<< " IS NOT STABLE!!!!!!!!!!!!!!!!!!" << this->stable << endl;
		for (viter i = this->neighbors.begin(); i != this->neighbors.end(); i++) {
			map<int, pair<int, int> > * copy = new map<int, pair<int, int> > ();
			*copy = this->RT;
			Message* m = new Message(copy, this->id, *(i), Message::RT);
			this->send(m);
		}
	} else {
//		cout << "=========NODE " << this->id << " IS STABLE============="
//				<< endl;
//		cout << printRT() << endl;
		/*stringstream s;
		 s<<"node "<<this->id<<" is stable"<<endl;
		 Message* msg = new Message(s.str(),this->id,0,Message::SYSTEM);
		 mailer->insertMsg(msg);*/
	}
}

void Node::distRT() {

	for (viter i = this->neighbors.begin(); i != this->neighbors.end(); i++) {
		map<int, pair<int, int> > * copy = new map<int, pair<int, int> > ();
		*copy = this->RT;
		Message* m = new Message(copy, this->id, *(i), Message::RT);
		//			cout<<"sending RT msg to my brother from another mother!"<<endl;
		this->send(m);
	}
}
void Node::run() {
	yield();
	//	cout<<"node: "<<this->id<<": wake up it's a beautiful morning..."<<endl;
	//send RT msg to all my neighbors
	if (!stable) {
		distRT();
		//		cout<<"RT SENT!"<<endl;
	}
	while (active) {
		if (this->get_sys_inbox_size()) {
			popMsg();
			//			if (!active)
			//				cout << "node " << this->id << " got no active" << endl;
		} else if (this->get_inbox_size()) {
			this->recv(NULL);
		} else { //no msgs at all
			//			cout << "node: " << this->id << " no messages at all - yielding..."
			//					<< endl;
			this->studendYield();
		}
	}
	stringstream s;
	s << "node " << this->id << " is dead" << endl;
	//	cout << "AAAAA " << s.str() << endl;
	Message* msg = new Message(s.str(), this->id, 0, Message::SYSTEM);
	mailer->insertMsg(this, msg);
	//cout<<s.str()<<endl;
	inbox_clear();
	sys_inbox_clear();
}

bool Node::studentSend(void *args) {

	Message* m = ((Message*) args);

	//		cout << "node: " << this->id << " at student send" << endl;
	//		cout << m.toString() << endl;
	mailer->insertMsg(this, m);

	return true;
}

void Node::studendYield() {
	this->yielded = true;
	this->yield();
}

bool Node::studentRecv(void **args) {
	popMsg();
	return true;
}

void Node::handleSysMsg(Message* msg) {
//	cout << "node: " << this->id << " handeling sys mesage" << endl;
	if (msg->kill) {
		stringstream s(msg->msg);
		int id;
		s >> id;
		if (id != this->id) {
//			cout << "kill msg for " << msg->msg << endl;
			resetRT(RT.size());
			distRT();
		} else
			this->active = false;
	} else if (msg->revive) {
		//		cout<<"node "<<this->id<<"reviving!!!"<<endl;
		//		this->active=true;
		//		resetRT(RT.size());
		//		distRT();
	}
//	cout << "Node::handleSysMsg, node " << this->id << " deleting sys msg"
//			<< endl;
	delete msg;
}
void Node::accumulate() {
	quantaAccumulator++;
	accumulator++;
}

void Node::studentSetQuanta(int n) {
	this->quantaAccumulator = 0;
	setQuanta(n);
}

void Node::handleRTMsg(Message* msg) {
	//		cout << "node: " << this->id << " handeling RT message" << endl;

	//	cout << "from : " << msg.sender << endl;
	//cout << "with RT: " << msg.printRT() << endl;
	map<int, pair<int, int> > * msgRT = new map<int, pair<int, int> > ();
	*msgRT = *msg->RTmsg;
	int msgSender = msg->sender;
	delete msg;
	updateRT(msgRT, msgSender);

	/*for (i = 1; i < this->neighbors.size(); i++) {
	 if (i != this->id) {
	 Message* m = new Message(this->RT, this->id, i, 1);
	 send(m);
	 }
	 }*/

}
void Node::handleMsg(Message* msg) {
	//		cout << "node: " << this->id << " handeling Regular message" << endl;
	stringstream result;
	if (msg->reciver != this->id) {
		result << this->id << " is sending “" << msg->msg << "” to "
				<< msg->reciver << " through " << this->RT[msg->reciver].second
				<< "," << msg->ballCounter << endl;
		;
		//cout<<result.str()<<endl;

		mailer->insertMsg(this,
				new Message(result.str(), 0, 0, Message::SYSTEM));

		msg->history.push_back(this->id);
		msg->sender = this->RT[msg->reciver].second;
		send(msg);
	} else {
		result << this->id << " received “" << msg->msg << "” from "
				<< msg->history.front() << " after " << msg->ballCounter
				<< " steps" << endl;
		mailer->insertMsg(this,
				new Message(result.str(), 0, 0, Message::SYSTEM));
		delete msg;
		//cout<<this->id<<" received “"<<msg.msg<<"” from "<<msg.history.front()<<" after "<<msg.ballCounter<<" steps"<<endl;
	}

}
